// JSON-RPC 相关支持
// 模块中实现的 JSON-RPC 不是标准的 JSON-RPC，而是一种简化版

/* eslint-disable no-use-before-define */

import { sendLine, registerLineHandler } from './backend.mjs';

const pendingRequests = [];
let currentRequestId = 1;

/**
 * @typedef JSONRPCError JSON-RPC 调用错误（与 JSON-RPC 规范中定义相同）
 *
 * @property {number} code 错误代码
 * @property {string} message 错误消息
 * @property {any} data 其他错误信息
 */

/**
 * 向后端发送一个请求 (request)。
 *
 * @param {string} method 请求方法
 * @param {object} params 请求参数
 * @returns {Promise<any>}
 */
function sendRequest(method, params = null) {
  const id = generateId();
  const payload = constructPayload(method, params, id);
  sendPayload(payload);
  return new Promise((resolve, reject) => {
    pendingRequests[id] = {
      resolve,
      reject,
    };
  });
}

/**
 * 向后端发送一个通知 (notification)。
 *
 * @param {string} method 通知方法
 * @param {object} params 通知参数
 * @returns {void}
 */
function sendNotification(method, params = null) {
  const payload = constructPayload(method, params);
  sendPayload(payload);
}

/** @private */
function constructPayload(method, params, id = undefined) {
  const res = {
    jsonrpc: '2.0',
    method,
    params,
  };
  if (id !== undefined) {
    res.id = id;
  }
  return res;
}

/** @private */
function sendPayload(payload) {
  sendLine(JSON.stringify(payload, undefined, 0));
}

/** @private */
function generateId() {
  return currentRequestId++; // eslint-disable-line no-plusplus
}

/** @private */
function dispatchBackendLineOutput(line) {
  const trimmedLine = line.trim();
  if (trimmedLine && trimmedLine.charAt(0) === '{') {
    let id = null;
    try {
      const payload = JSON.parse(line);
      id = payload.id; // eslint-disable-line prefer-destructuring
      verifyResponsePayload(payload);
      const { result, error } = payload;
      if (result !== undefined) {
        pendingRequests[id].resolve(result);
      } else if (error !== undefined) {
        pendingRequests[id].reject(error);
      } else {
        throw Error('应不会到达此处');
      }
      pendingRequests[id] = undefined;
    } catch (error) {
      if (id !== undefined && id !== null && pendingRequests[id] !== undefined) {
        pendingRequests[id].reject(error);
      } else {
        console.error('处理后端响应时出错:', error);
      }
    }
  }
}

/** @private */
function verifyResponsePayload(payload) {
  if (typeof payload !== 'object') {
    throw Error('后端响应必须为 JSON 对象');
  }
  const {
    jsonrpc,
    id,
    result,
    error,
  } = payload;
  if (jsonrpc !== '2.0') {
    throw Error('后端响应 "jsonrpc" 字段无效');
  }
  if (id === undefined || pendingRequests[id] === undefined) {
    throw Error('后端响应 "id" 字段无效');
  }
  if (result === undefined && error === undefined) {
    throw Error('后端响应无 "result" 或 "error" 字段');
  }
}

registerLineHandler(dispatchBackendLineOutput);

export {
  sendRequest,
  sendNotification,
};
